feat(ai): refresh landing ecosystem animation#940
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRefactors AILibraryHero to data-driven SVG rendering, adds per-framework metadata and helpers, updates animation geometry and selection flow, integrates helpers into framework cards/routes, refreshes marketing content, improves docs filesystem helper, and adds tests for framework metadata. ChangesAI Hero Data-Driven Refactoring
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/libraries/ai.tsx (1)
16-49: ⚖️ Poor tradeoffConsider consolidating overlapping feature content.
The
featureHighlightsarray here partially overlaps with the 8 hardcoded feature cards inAiLanding.tsx(lines 64-121). Topics like "Provider Agnostic," "AG-UI Native," and "Typed Tools" appear in both locations with similar but not identical wording. This duplication creates a maintenance burden if the messaging needs to stay synchronized or evolve together.Consider whether the two sections could reference a single source of truth, or document the intended distinction between the LibraryFeatureHighlights section and the ecosystem grid cards.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/libraries/ai.tsx` around lines 16 - 49, The featureHighlights array in src/libraries/ai.tsx duplicates messaging found in the hardcoded feature cards in AiLanding.tsx; extract the shared feature data into a single exported constant (e.g., export const LIBRARY_FEATURES) and have both LibraryFeatureHighlights (featureHighlights) and the AiLanding card grid import and use that constant, or alternatively add brief comments/descriptions to each source to document the intended distinction if they must remain different; update references in the component that currently uses featureHighlights and in AiLanding.tsx to consume the new shared symbol so messaging is maintained in one place.src/components/landing/AiLanding.tsx (1)
66-70: 💤 Low valueConsider mentioning Vanilla framework.
The stack context indicates Vanilla is a supported framework alongside React, Vue, Solid, Svelte, and Preact, but it's not mentioned in the "Client Agnostic" card or elsewhere in the marketing copy. Verify whether Vanilla should be included or if its omission is intentional.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/landing/AiLanding.tsx` around lines 66 - 70, The marketing copy in the AiLanding component’s "Client Agnostic" card omits "Vanilla" from the list of supported frameworks; update the paragraph text in the AiLanding component (the <h4> with "Client Agnostic" and its following <p>) to include "Vanilla" alongside React, Vue, Solid, Svelte, and Preact (e.g., "React, Vue, Solid, Svelte, Preact, and Vanilla") or, if omission was intentional, add a brief comment in the AiLanding component explaining why Vanilla is excluded so reviewers know it’s deliberate.src/components/AILibraryHero.tsx (1)
243-278: 💤 Low value
pulse-downandpulse-upkeyframes are identical—direction has no visual effect.Both keyframe definitions at lines 243–256 and 257–270 produce the same animation. If the intent is to visually differentiate the pulse direction (e.g., gradient offset, translateY, or brightness sweep direction), the keyframes need distinct definitions. Otherwise,
connectionPulseDirectiontoggling at line 185 is effectively dead logic.If both directions should look the same, consider consolidating into a single
pulseanimation to reduce duplication.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/AILibraryHero.tsx` around lines 243 - 278, The two keyframes pulse-down and pulse-up are identical so toggling connectionPulseDirection has no effect; either make the animations visually different (e.g., change transform/translateY, gradient offset, or staggered filter/brightness values in pulse-down vs pulse-up) and keep both keyframes and the classes (.animate-pulse-down, .animate-pulse-up), or consolidate them into a single pulse keyframe and replace usages of pulse-down/pulse-up (and the connectionPulseDirection toggle) with that single animation to remove dead logic; update the animate-pulse-* class definitions and any code referencing connectionPulseDirection accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/components/AILibraryHero.tsx`:
- Around line 243-278: The two keyframes pulse-down and pulse-up are identical
so toggling connectionPulseDirection has no effect; either make the animations
visually different (e.g., change transform/translateY, gradient offset, or
staggered filter/brightness values in pulse-down vs pulse-up) and keep both
keyframes and the classes (.animate-pulse-down, .animate-pulse-up), or
consolidate them into a single pulse keyframe and replace usages of
pulse-down/pulse-up (and the connectionPulseDirection toggle) with that single
animation to remove dead logic; update the animate-pulse-* class definitions and
any code referencing connectionPulseDirection accordingly.
In `@src/components/landing/AiLanding.tsx`:
- Around line 66-70: The marketing copy in the AiLanding component’s "Client
Agnostic" card omits "Vanilla" from the list of supported frameworks; update the
paragraph text in the AiLanding component (the <h4> with "Client Agnostic" and
its following <p>) to include "Vanilla" alongside React, Vue, Solid, Svelte, and
Preact (e.g., "React, Vue, Solid, Svelte, Preact, and Vanilla") or, if omission
was intentional, add a brief comment in the AiLanding component explaining why
Vanilla is excluded so reviewers know it’s deliberate.
In `@src/libraries/ai.tsx`:
- Around line 16-49: The featureHighlights array in src/libraries/ai.tsx
duplicates messaging found in the hardcoded feature cards in AiLanding.tsx;
extract the shared feature data into a single exported constant (e.g., export
const LIBRARY_FEATURES) and have both LibraryFeatureHighlights
(featureHighlights) and the AiLanding card grid import and use that constant, or
alternatively add brief comments/descriptions to each source to document the
intended distinction if they must remain different; update references in the
component that currently uses featureHighlights and in AiLanding.tsx to consume
the new shared symbol so messaging is maintained in one place.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0af7429e-9867-422f-b706-1d270dbadfdf
📒 Files selected for processing (12)
src/components/AILibraryHero.tsxsrc/components/AILibraryHeroBox.tsxsrc/components/FrameworkCard.tsxsrc/components/landing/AiLanding.tsxsrc/hooks/useAILibraryHeroAnimation.tssrc/libraries/ai.tsxsrc/libraries/frameworkSupport.tssrc/libraries/libraries.tssrc/libraries/types.tssrc/routes/$libraryId/$version.docs.framework.index.tsxsrc/stores/aiLibraryHeroAnimation.tstests/ai-framework-doc-links.test.ts
7553b0a to
2494633
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/hooks/useAILibraryHeroAnimation.ts (1)
181-210: 💤 Low valueService rotation duplicates
runRotationlogic.The custom service rotation loop (lines 185-207) largely duplicates the
runRotationhelper with different timing constants. Consider parameterizingrunRotationto accept timing configuration, which would reduce duplication and ensure consistent behavior.That said, the current implementation is functionally correct and the timing differences are intentional.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useAILibraryHeroAnimation.ts` around lines 181 - 210, The rotateService function duplicates runRotation logic; refactor by adding parameters to runRotation (e.g., startIndex, rotations, timingConfig, targetService, onComplete) and reuse it instead of rotateService: call runRotation(currentServiceIndex, serviceRotations, {fast: TIMING.serviceRotationFast, slowBase: TIMING.serviceRotationSlowBase, slowInc: TIMING.serviceRotationSlowIncrement}, targetService, onComplete) so addTimeout, store.setRotatingService, index incrementing, final selection behavior and TIMING-based delay computation live in runRotation and the current rotateService block is removed; keep references to SERVICES_COUNT, SERVICE_WIDTH, SERVICE_GUTTER, addTimeout and store.* calls inside runRotation to preserve behavior.src/components/AILibraryHero.tsx (1)
502-502: 💤 Low valueMagic numbers in path control points.
The Bezier curve control point Y-coordinates (
322and424) are hardcoded. Consider deriving these from existing layout constants (e.g., based onSERVER_CARD_Y_OFFSETandSERVICE_Y_OFFSET) for maintainability if the layout geometry changes again.+const SERVER_TO_TANSTACK_CONTROL_Y = (TANSTACK_AI_BOX.y + TANSTACK_AI_BOX.height + SERVER_CARD_Y_OFFSET) / 2 +const SERVER_TO_PROVIDER_CONTROL_Y = (SERVER_CARD_Y_OFFSET + SERVER_CARD_HEIGHT + SERVICE_Y_OFFSET) / 2Also applies to: 522-522
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/AILibraryHero.tsx` at line 502, The Bezier path strings use hardcoded control Y values (e.g., the `d` attribute building string in AILibraryHero that contains `Q ${serverBoxCenterX} 322 ${endX} ${SERVER_CARD_Y_OFFSET}` and the similar instance around line 522); replace those magic numbers with computed values derived from existing layout constants (for example compute controlY as a function of TANSTACK_AI_BOX.y, TANSTACK_AI_BOX.height, SERVER_CARD_Y_OFFSET, and SERVICE_Y_OFFSET or an average/midpoint between the source and target Y positions) and use that computed variable in the template literal so control points adapt when SERVER_CARD_Y_OFFSET or SERVICE_Y_OFFSET changes.src/utils/documents.server.ts (1)
129-135: ⚡ Quick winConsider adding a documentation comment.
The
../../..navigation assumes a specific directory structure (sibling repositories in local development). A brief comment explaining the path resolution would help future maintainers.📝 Suggested documentation
+/** + * Resolve the base directory for a local repository. + * Assumes repos are siblings to the main project: + * parent/tanstack.com/src/utils/documents.server.ts (this file) + * parent/{repo}/ (target repo) + */ function getLocalRepoBaseDir(repo: string) { return path.resolve( path.dirname(fileURLToPath(import.meta.url)), '../../..', repo, ) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/utils/documents.server.ts` around lines 129 - 135, Add a brief documentation comment above the getLocalRepoBaseDir function explaining that it resolves a local sibling repository path by walking up three directories via '../../..' from the current file (fileURLToPath(import.meta.url)) and that this assumes the monorepo/project layout used in local development; mention expected repo layout and a caution that changing project structure requires updating this relative path. Ensure the comment references getLocalRepoBaseDir and the '../../..' segment so future maintainers understand the assumption and where to update if layout changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/landing/AiLanding.tsx`:
- Around line 60-64: The copy in the AiLanding component has a subject-verb
agreement error: the phrase "TanStack AI is open-source libraries and
AG-UI-compatible standards" should be revised so the verb agrees with the plural
compound subject; update the text in the AiLanding component (the paragraph
containing "TanStack AI is open-source libraries and AG-UI-compatible
standards...") to either change "is" to "are" or rephrase to "TanStack AI
consists of open-source libraries and AG-UI-compatible standards" (or similar)
to fix the grammar while preserving the original meaning.
---
Nitpick comments:
In `@src/components/AILibraryHero.tsx`:
- Line 502: The Bezier path strings use hardcoded control Y values (e.g., the
`d` attribute building string in AILibraryHero that contains `Q
${serverBoxCenterX} 322 ${endX} ${SERVER_CARD_Y_OFFSET}` and the similar
instance around line 522); replace those magic numbers with computed values
derived from existing layout constants (for example compute controlY as a
function of TANSTACK_AI_BOX.y, TANSTACK_AI_BOX.height, SERVER_CARD_Y_OFFSET, and
SERVICE_Y_OFFSET or an average/midpoint between the source and target Y
positions) and use that computed variable in the template literal so control
points adapt when SERVER_CARD_Y_OFFSET or SERVICE_Y_OFFSET changes.
In `@src/hooks/useAILibraryHeroAnimation.ts`:
- Around line 181-210: The rotateService function duplicates runRotation logic;
refactor by adding parameters to runRotation (e.g., startIndex, rotations,
timingConfig, targetService, onComplete) and reuse it instead of rotateService:
call runRotation(currentServiceIndex, serviceRotations, {fast:
TIMING.serviceRotationFast, slowBase: TIMING.serviceRotationSlowBase, slowInc:
TIMING.serviceRotationSlowIncrement}, targetService, onComplete) so addTimeout,
store.setRotatingService, index incrementing, final selection behavior and
TIMING-based delay computation live in runRotation and the current rotateService
block is removed; keep references to SERVICES_COUNT, SERVICE_WIDTH,
SERVICE_GUTTER, addTimeout and store.* calls inside runRotation to preserve
behavior.
In `@src/utils/documents.server.ts`:
- Around line 129-135: Add a brief documentation comment above the
getLocalRepoBaseDir function explaining that it resolves a local sibling
repository path by walking up three directories via '../../..' from the current
file (fileURLToPath(import.meta.url)) and that this assumes the monorepo/project
layout used in local development; mention expected repo layout and a caution
that changing project structure requires updating this relative path. Ensure the
comment references getLocalRepoBaseDir and the '../../..' segment so future
maintainers understand the assumption and where to update if layout changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6bb16b72-7339-4bb3-bdff-5f642300afac
⛔ Files ignored due to path filters (8)
src/images/ag-ui-dark.svgis excluded by!**/*.svgsrc/images/ag-ui-light.svgis excluded by!**/*.svgsrc/images/elevenlabs-dark.svgis excluded by!**/*.svgsrc/images/elevenlabs-light.svgis excluded by!**/*.svgsrc/images/fal-ai-dark.svgis excluded by!**/*.svgsrc/images/fal-ai-light.svgis excluded by!**/*.svgsrc/images/xai-dark.svgis excluded by!**/*.svgsrc/images/xai-light.svgis excluded by!**/*.svg
📒 Files selected for processing (13)
src/components/AILibraryHero.tsxsrc/components/AILibraryHeroBox.tsxsrc/components/FrameworkCard.tsxsrc/components/landing/AiLanding.tsxsrc/hooks/useAILibraryHeroAnimation.tssrc/libraries/ai.tsxsrc/libraries/frameworkSupport.tssrc/libraries/libraries.tssrc/libraries/types.tssrc/routes/$libraryId/$version.docs.framework.index.tsxsrc/stores/aiLibraryHeroAnimation.tssrc/utils/documents.server.tstests/ai-framework-doc-links.test.ts
| TanStack AI is open-source libraries and AG-UI-compatible standards, | ||
| not a hosted gateway. Bring your client framework, your server | ||
| runtime, and the AI providers you trust. There is no middleman, no | ||
| service fee, and no vendor lock-in, just composable tools built for | ||
| teams that want to own their AI stack. |
There was a problem hiding this comment.
Fix subject-verb agreement.
The sentence "TanStack AI is open-source libraries and AG-UI-compatible standards" has a grammatical error: the singular verb "is" doesn't agree with the plural compound subject "libraries and standards."
✏️ Proposed fix
- TanStack AI is open-source libraries and AG-UI-compatible standards,
+ TanStack AI provides open-source libraries and AG-UI-compatible standards,Alternatively:
- TanStack AI is open-source libraries and AG-UI-compatible standards,
+ TanStack AI is a set of open-source libraries and AG-UI-compatible standards,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| TanStack AI is open-source libraries and AG-UI-compatible standards, | |
| not a hosted gateway. Bring your client framework, your server | |
| runtime, and the AI providers you trust. There is no middleman, no | |
| service fee, and no vendor lock-in, just composable tools built for | |
| teams that want to own their AI stack. | |
| TanStack AI provides open-source libraries and AG-UI-compatible standards, | |
| not a hosted gateway. Bring your client framework, your server | |
| runtime, and the AI providers you trust. There is no middleman, no | |
| service fee, and no vendor lock-in, just composable tools built for | |
| teams that want to own their AI stack. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/landing/AiLanding.tsx` around lines 60 - 64, The copy in the
AiLanding component has a subject-verb agreement error: the phrase "TanStack AI
is open-source libraries and AG-UI-compatible standards" should be revised so
the verb agrees with the plural compound subject; update the text in the
AiLanding component (the paragraph containing "TanStack AI is open-source
libraries and AG-UI-compatible standards...") to either change "is" to "are" or
rephrase to "TanStack AI consists of open-source libraries and AG-UI-compatible
standards" (or similar) to fix the grammar while preserving the original
meaning.
0c0340a to
b436c01
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
src/components/landing/AiLanding.tsx (1)
60-64:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix subject-verb agreement.
The sentence has a grammatical error: the singular verb "is" doesn't agree with the plural compound subject "libraries and standards."
✏️ Proposed fix
- TanStack AI is open-source libraries and AG-UI-compatible standards, + TanStack AI provides open-source libraries and AG-UI-compatible standards,Alternatively:
- TanStack AI is open-source libraries and AG-UI-compatible standards, + TanStack AI is a set of open-source libraries and AG-UI-compatible standards,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/landing/AiLanding.tsx` around lines 60 - 64, In the AiLanding component update the sentence so subject and verb agree: change "TanStack AI is open-source libraries and AG-UI-compatible standards" to use a plural verb or rephrase (e.g., "TanStack AI are open-source libraries and AG-UI-compatible standards" or better "TanStack AI comprises open-source libraries and AG-UI-compatible standards")—edit the string in the AiLanding component where that paragraph is defined to use the corrected wording.
🧹 Nitpick comments (2)
src/libraries/frameworkSupport.ts (1)
3-23: ⚡ Quick winRemove
libraryIdfrom helper input to avoid split-source mismatches.
getFrameworkPackageNamecan derive everything fromlibrary.id; taking bothlibraryIdandlibrarymakes incorrect pairings possible and can silently produce wrong package names.♻️ Suggested change
export function getFrameworkPackageName( framework: Framework, - libraryId: LibraryId, library: LibrarySlim, ) { const packageName = library.frameworkPackageNames?.[framework] if (packageName) { return packageName } if (framework === 'vanilla') { - return library.corePackageName ?? `@tanstack/${libraryId}` + return library.corePackageName ?? `@tanstack/${library.id}` } - if (framework === 'angular' && libraryId === 'query') { + if (framework === 'angular' && library.id === 'query') { return '`@tanstack/angular-query-experimental`' } - return `@tanstack/${framework}-${libraryId}` + return `@tanstack/${framework}-${library.id}` }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/libraries/frameworkSupport.ts` around lines 3 - 23, The helper getFrameworkPackageName currently accepts both libraryId and library which allows mismatched pairs; change its signature to only accept (framework: Framework, library: LibrarySlim) and derive the id from library.id everywhere inside the function (use library.id in place of the former libraryId variable) while preserving the existing special-casing for 'vanilla' and the angular query exception; then update all call sites that passed a separate libraryId to pass only the library and framework (or extract library.id at the call site) so callers match the new signature and no longer risk inconsistent libraryId/library pairs.tests/ai-framework-doc-links.test.ts (1)
27-54: 🏗️ Heavy liftAdd an existence check for each docs path, not just string equality.
This test currently verifies configured mappings but doesn’t verify that each resolved docs path actually exists, so 404 regressions can still slip through while this test passes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/ai-framework-doc-links.test.ts` around lines 27 - 54, The test only compares configured strings (expectedDocsPaths) but doesn't assert those docs paths actually exist; update the loop that iterates expectedFrameworks to, after computing docsPath = getFrameworkDocsPath(framework, ai), resolve that logical docs path to the repo docs file (e.g. join process.cwd() with the docs root and check for either `${docsPath}.md` or `${docsPath}/index.md`) and add an assertion like assert.ok(fs.existsSync(resolvedPath), `Docs page for ${framework} exists`); you can encapsulate the filesystem check in a small helper (e.g. docsPathExists or resolveDocsFile) to keep the test readable and use Node's fs and path modules.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/AILibraryHero.tsx`:
- Around line 254-263: getProviderOpacity currently forces provider cards to be
fully inside the viewport before showing them, causing pop-in/out behavior;
change its logic to only hide when completely off-screen and otherwise return
getServiceOpacity so the SVG clipPath can handle edge clipping. Update
getProviderOpacity (referencing SERVICE_LOCATIONS, serviceOffset, SERVICE_WIDTH,
SVG_WIDTH, and getServiceOpacity) to compute x = SERVICE_LOCATIONS[index] +
serviceOffset and return 0 only when the card is entirely outside the lane
(e.g., x + SERVICE_WIDTH <= 0 or x >= SVG_WIDTH); otherwise return
getServiceOpacity(index).
In `@src/components/AILibraryHeroBox.tsx`:
- Around line 39-49: The left-aligned text is still indented because textX
always adds logoSize; update the textX calculation in AILibraryHeroBox to only
add the logo offset when a logo will actually be rendered (i.e., when showLogo
is true and a custom/separate logo exists via hasCustomLogo or
hasSeparateLogos), otherwise omit the logoSize so left-aligned labels are flush;
adjust the expression that computes textX (and any related layout that uses
logoSize) to guard with showLogo && (hasCustomLogo || hasSeparateLogos).
In `@src/hooks/useAILibraryHeroAnimation.ts`:
- Around line 185-205: The rotation currently only updates serviceOffset after
all steps, causing intermediate rotatingService indices to be off-canvas; modify
rotateService so that on every iteration (inside rotateService) you call
store.setServiceOffset with the offset computed from the current rotating index
(use -(currentIndex * (SERVICE_WIDTH + SERVICE_GUTTER))) so the active provider
stays centered during each step, and ensure you also set the offset for the
final targetService before the post-selection pause (right before calling
setSelectedService and setRotatingService(null)); keep existing
setRotatingService calls and timing logic but add the per-step setServiceOffset
updates using the same offset formula.
In `@src/utils/documents.server.ts`:
- Around line 129-135: The getLocalRepoBaseDir function must validate the repo
parameter to prevent path traversal: reject or throw for repo values containing
"..", absolute paths, path separators that escape the expected "owner/repo"
form, or any characters outside an allowed pattern (e.g., only letters, numbers,
dots, hyphens, underscores and a single slash). Update getLocalRepoBaseDir to
validate repo first (and fail-fast) and/or normalize and re-check that the
resolved path remains inside the intended repo root (so downstream checks like
localFilePath.startsWith(baseDir) are meaningful); reference getLocalRepoBaseDir
when adding the validation and ensure callers cannot pass an unsafe repo string.
---
Duplicate comments:
In `@src/components/landing/AiLanding.tsx`:
- Around line 60-64: In the AiLanding component update the sentence so subject
and verb agree: change "TanStack AI is open-source libraries and
AG-UI-compatible standards" to use a plural verb or rephrase (e.g., "TanStack AI
are open-source libraries and AG-UI-compatible standards" or better "TanStack AI
comprises open-source libraries and AG-UI-compatible standards")—edit the string
in the AiLanding component where that paragraph is defined to use the corrected
wording.
---
Nitpick comments:
In `@src/libraries/frameworkSupport.ts`:
- Around line 3-23: The helper getFrameworkPackageName currently accepts both
libraryId and library which allows mismatched pairs; change its signature to
only accept (framework: Framework, library: LibrarySlim) and derive the id from
library.id everywhere inside the function (use library.id in place of the former
libraryId variable) while preserving the existing special-casing for 'vanilla'
and the angular query exception; then update all call sites that passed a
separate libraryId to pass only the library and framework (or extract library.id
at the call site) so callers match the new signature and no longer risk
inconsistent libraryId/library pairs.
In `@tests/ai-framework-doc-links.test.ts`:
- Around line 27-54: The test only compares configured strings
(expectedDocsPaths) but doesn't assert those docs paths actually exist; update
the loop that iterates expectedFrameworks to, after computing docsPath =
getFrameworkDocsPath(framework, ai), resolve that logical docs path to the repo
docs file (e.g. join process.cwd() with the docs root and check for either
`${docsPath}.md` or `${docsPath}/index.md`) and add an assertion like
assert.ok(fs.existsSync(resolvedPath), `Docs page for ${framework} exists`); you
can encapsulate the filesystem check in a small helper (e.g. docsPathExists or
resolveDocsFile) to keep the test readable and use Node's fs and path modules.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6a956060-d586-496b-9daf-144012013f1c
⛔ Files ignored due to path filters (10)
src/images/ag-ui-dark.svgis excluded by!**/*.svgsrc/images/ag-ui-light.svgis excluded by!**/*.svgsrc/images/elevenlabs-dark.svgis excluded by!**/*.svgsrc/images/elevenlabs-light.svgis excluded by!**/*.svgsrc/images/fal-ai-dark.svgis excluded by!**/*.svgsrc/images/fal-ai-light.svgis excluded by!**/*.svgsrc/images/groq-dark.svgis excluded by!**/*.svgsrc/images/groq-light.svgis excluded by!**/*.svgsrc/images/xai-dark.svgis excluded by!**/*.svgsrc/images/xai-light.svgis excluded by!**/*.svg
📒 Files selected for processing (13)
src/components/AILibraryHero.tsxsrc/components/AILibraryHeroBox.tsxsrc/components/FrameworkCard.tsxsrc/components/landing/AiLanding.tsxsrc/hooks/useAILibraryHeroAnimation.tssrc/libraries/ai.tsxsrc/libraries/frameworkSupport.tssrc/libraries/libraries.tssrc/libraries/types.tssrc/routes/$libraryId/$version.docs.framework.index.tsxsrc/stores/aiLibraryHeroAnimation.tssrc/utils/documents.server.tstests/ai-framework-doc-links.test.ts
✅ Files skipped from review due to trivial changes (1)
- src/libraries/ai.tsx
| showLogo = true, | ||
| logoLight, | ||
| logoDark, | ||
| logo, | ||
| logoSize = 40, | ||
| centerText = false, | ||
| }: AILibraryHeroBoxProps) { | ||
| // For centerText, align logo and text higher up; otherwise use normal center | ||
| const textX = 25 + logoSize | ||
| const textY = 15 + fontSize | ||
| const hasCustomLogo = logo || logoLight || logoDark | ||
| const hasSeparateLogos = !logo && logoLight && logoDark | ||
| const textX = centerText ? width / 2 : 25 + logoSize | ||
| const textY = height / 2 + fontSize * 0.35 |
There was a problem hiding this comment.
showLogo={false} still leaves the label indented.
The new prop hides the image, but textX still reserves logoSize, so left-aligned labels render shifted right when the logo is disabled.
Suggested diff
- const textX = centerText ? width / 2 : 25 + logoSize
+ const hasVisibleLogo = showLogo
+ const textX = centerText ? width / 2 : hasVisibleLogo ? 25 + logoSize : 25📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| showLogo = true, | |
| logoLight, | |
| logoDark, | |
| logo, | |
| logoSize = 40, | |
| centerText = false, | |
| }: AILibraryHeroBoxProps) { | |
| // For centerText, align logo and text higher up; otherwise use normal center | |
| const textX = 25 + logoSize | |
| const textY = 15 + fontSize | |
| const hasCustomLogo = logo || logoLight || logoDark | |
| const hasSeparateLogos = !logo && logoLight && logoDark | |
| const textX = centerText ? width / 2 : 25 + logoSize | |
| const textY = height / 2 + fontSize * 0.35 | |
| showLogo = true, | |
| logoLight, | |
| logoDark, | |
| logo, | |
| logoSize = 40, | |
| centerText = false, | |
| }: AILibraryHeroBoxProps) { | |
| const hasCustomLogo = logo || logoLight || logoDark | |
| const hasSeparateLogos = !logo && logoLight && logoDark | |
| const hasVisibleLogo = showLogo | |
| const textX = centerText ? width / 2 : hasVisibleLogo ? 25 + logoSize : 25 | |
| const textY = height / 2 + fontSize * 0.35 |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/AILibraryHeroBox.tsx` around lines 39 - 49, The left-aligned
text is still indented because textX always adds logoSize; update the textX
calculation in AILibraryHeroBox to only add the logo offset when a logo will
actually be rendered (i.e., when showLogo is true and a custom/separate logo
exists via hasCustomLogo or hasSeparateLogos), otherwise omit the logoSize so
left-aligned labels are flush; adjust the expression that computes textX (and
any related layout that uses logoSize) to guard with showLogo && (hasCustomLogo
|| hasSeparateLogos).
| function getLocalRepoBaseDir(repo: string) { | ||
| return path.resolve( | ||
| path.dirname(fileURLToPath(import.meta.url)), | ||
| '../../..', | ||
| repo, | ||
| ) | ||
| } |
There was a problem hiding this comment.
Validate repo parameter to prevent path traversal.
If repo contains .. segments (e.g., from a malicious repoPair like "TanStack/../../etc"), the returned base directory escapes the intended scope. Downstream checks like localFilePath.startsWith(baseDir) in fetchFs become ineffective because baseDir itself is compromised.
🛡️ Proposed fix: Add validation
function getLocalRepoBaseDir(repo: string) {
+ const normalized = path.normalize(repo)
+ if (normalized.startsWith('..') || normalized.includes('/..') || path.isAbsolute(normalized)) {
+ throw new Error(`Invalid repo name: ${repo}`)
+ }
return path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'../../..',
repo,
)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function getLocalRepoBaseDir(repo: string) { | |
| return path.resolve( | |
| path.dirname(fileURLToPath(import.meta.url)), | |
| '../../..', | |
| repo, | |
| ) | |
| } | |
| function getLocalRepoBaseDir(repo: string) { | |
| const normalized = path.normalize(repo) | |
| if (normalized.startsWith('..') || normalized.includes('/..') || path.isAbsolute(normalized)) { | |
| throw new Error(`Invalid repo name: ${repo}`) | |
| } | |
| return path.resolve( | |
| path.dirname(fileURLToPath(import.meta.url)), | |
| '../../..', | |
| repo, | |
| ) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/documents.server.ts` around lines 129 - 135, The
getLocalRepoBaseDir function must validate the repo parameter to prevent path
traversal: reject or throw for repo values containing "..", absolute paths, path
separators that escape the expected "owner/repo" form, or any characters outside
an allowed pattern (e.g., only letters, numbers, dots, hyphens, underscores and
a single slash). Update getLocalRepoBaseDir to validate repo first (and
fail-fast) and/or normalize and re-check that the resolved path remains inside
the intended repo root (so downstream checks like
localFilePath.startsWith(baseDir) are meaningful); reference getLocalRepoBaseDir
when adding the validation and ensure callers cannot pass an unsafe repo string.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/ai-framework-doc-links.test.ts (1)
4-7: ⚡ Quick winAdd coverage for
getFrameworkDocsHashto lock full link behavior.
FrameworkCardnow depends on docs path + hash together. Extending this test to assert hash behavior will prevent regressions where links resolve to wrong anchors.Proposed test extension
import { + getFrameworkDocsHash, getFrameworkDocsPath, getFrameworkPackageName, } from '../src/libraries/frameworkSupport' +const expectedDocsHashes: Partial<Record<Framework, string | undefined>> = { + react: undefined, + vue: undefined, + solid: undefined, + svelte: undefined, + preact: undefined, + vanilla: undefined, +} + for (const framework of expectedFrameworks) { assert.equal( getFrameworkPackageName(framework, ai.id, ai), expectedPackages[framework], `${framework} package name points to the shipped AI package`, ) assert.equal( getFrameworkDocsPath(framework, ai), expectedDocsPaths[framework], `${framework} framework card links to an existing AI docs page`, ) + + assert.equal( + getFrameworkDocsHash(framework, ai), + expectedDocsHashes[framework], + `${framework} docs hash matches framework-link contract`, + ) }Also applies to: 42-54
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/ai-framework-doc-links.test.ts` around lines 4 - 7, Extend the existing test to call getFrameworkDocsHash in addition to getFrameworkDocsPath and getFrameworkPackageName and assert that FrameworkCard-style links are built using the docs path concatenated with the returned hash (i.e., verify `${getFrameworkDocsPath(...) }#${ getFrameworkDocsHash(...) }` or equivalent is produced); update the test to cover multiple frameworks (reuse existing test inputs) and add assertions that the final link equals the expected path+hash string so both path and hash behavior are locked down (refer to functions getFrameworkDocsHash, getFrameworkDocsPath, getFrameworkPackageName and the FrameworkCard link behavior).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/ai-framework-doc-links.test.ts`:
- Around line 27-53: The test currently only checks getFrameworkDocsPath against
hardcoded expectedDocsPaths but doesn't assert the target docs actually exist;
update the test to load the docs route/index used by the site build (the same
source the site uses) and for each framework call
getFrameworkDocsPath(framework, ai) and assert that the returned path is present
in that route index (e.g. docsRoutes.includes(resolvedPath) or
routeIndex.has(resolvedPath)); keep the existing assertions but add this
existence check with a clear failure message referencing the framework and
resolvedPath so renamed/removed pages fail the test; locate this change next to
the loop that iterates expectedFrameworks and uses getFrameworkDocsPath.
---
Nitpick comments:
In `@tests/ai-framework-doc-links.test.ts`:
- Around line 4-7: Extend the existing test to call getFrameworkDocsHash in
addition to getFrameworkDocsPath and getFrameworkPackageName and assert that
FrameworkCard-style links are built using the docs path concatenated with the
returned hash (i.e., verify `${getFrameworkDocsPath(...) }#${
getFrameworkDocsHash(...) }` or equivalent is produced); update the test to
cover multiple frameworks (reuse existing test inputs) and add assertions that
the final link equals the expected path+hash string so both path and hash
behavior are locked down (refer to functions getFrameworkDocsHash,
getFrameworkDocsPath, getFrameworkPackageName and the FrameworkCard link
behavior).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: afeb967d-ebc3-41fb-b20f-999d49ef6d49
⛔ Files ignored due to path filters (10)
src/images/ag-ui-dark.svgis excluded by!**/*.svgsrc/images/ag-ui-light.svgis excluded by!**/*.svgsrc/images/elevenlabs-dark.svgis excluded by!**/*.svgsrc/images/elevenlabs-light.svgis excluded by!**/*.svgsrc/images/fal-ai-dark.svgis excluded by!**/*.svgsrc/images/fal-ai-light.svgis excluded by!**/*.svgsrc/images/groq-dark.svgis excluded by!**/*.svgsrc/images/groq-light.svgis excluded by!**/*.svgsrc/images/xai-dark.svgis excluded by!**/*.svgsrc/images/xai-light.svgis excluded by!**/*.svg
📒 Files selected for processing (13)
src/components/AILibraryHero.tsxsrc/components/AILibraryHeroBox.tsxsrc/components/FrameworkCard.tsxsrc/components/landing/AiLanding.tsxsrc/hooks/useAILibraryHeroAnimation.tssrc/libraries/ai.tsxsrc/libraries/frameworkSupport.tssrc/libraries/libraries.tssrc/libraries/types.tssrc/routes/$libraryId/$version.docs.framework.index.tsxsrc/stores/aiLibraryHeroAnimation.tssrc/utils/documents.server.tstests/ai-framework-doc-links.test.ts
| const expectedDocsPaths: Partial<Record<Framework, string>> = { | ||
| react: 'getting-started/quick-start', | ||
| vue: 'getting-started/quick-start-vue', | ||
| solid: 'api/ai-solid', | ||
| svelte: 'getting-started/quick-start-svelte', | ||
| preact: 'api/ai-preact', | ||
| vanilla: 'api/ai-client', | ||
| } | ||
|
|
||
| assert.deepEqual( | ||
| ai.frameworks, | ||
| expectedFrameworks, | ||
| 'AI framework list reflects shipped framework packages', | ||
| ) | ||
|
|
||
| for (const framework of expectedFrameworks) { | ||
| assert.equal( | ||
| getFrameworkPackageName(framework, ai.id, ai), | ||
| expectedPackages[framework], | ||
| `${framework} package name points to the shipped AI package`, | ||
| ) | ||
|
|
||
| assert.equal( | ||
| getFrameworkDocsPath(framework, ai), | ||
| expectedDocsPaths[framework], | ||
| `${framework} framework card links to an existing AI docs page`, | ||
| ) |
There was a problem hiding this comment.
Test does not verify docs routes actually exist.
These assertions only validate helper output against hardcoded strings, so the test still passes if a mapped page is deleted/renamed and links 404 in production. Please add an existence check for each resolved docs path against the docs source/route index used by the site build.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/ai-framework-doc-links.test.ts` around lines 27 - 53, The test
currently only checks getFrameworkDocsPath against hardcoded expectedDocsPaths
but doesn't assert the target docs actually exist; update the test to load the
docs route/index used by the site build (the same source the site uses) and for
each framework call getFrameworkDocsPath(framework, ai) and assert that the
returned path is present in that route index (e.g.
docsRoutes.includes(resolvedPath) or routeIndex.has(resolvedPath)); keep the
existing assertions but add this existence check with a clear failure message
referencing the framework and resolvedPath so renamed/removed pages fail the
test; locate this change next to the loop that iterates expectedFrameworks and
uses getFrameworkDocsPath.
59613d2 to
c72d7eb
Compare
c72d7eb to
e773b52
Compare
Summary
Validation
Summary by CodeRabbit
New Features
Updates
Tests